My book about localization with Xamarin is out

Cross-platform Localization for Native Mobile Apps with Xamarin
Cross-platform Localization for Native Mobile Apps with Xamarin

Last month Apress published my book on writing localized apps with Xamarin. It’s titled “Cross-platform Localization for Native Mobile Apps with Xamarin” and are on Apress’s site or on Amazon

It’s not a long book, about 110 pages.  It provide the basic information you would need to localize your app for other languages and countries.  It’s written for the Xamarin developer, but the topics apply to other developers.

After discussing the basics of localization, the book covers how to get your text translated.  There is a chapter that covers the Multilingual App Toolkit (aka MAT), which is Visual Studio extension that is a workflow management tool for language resource files.

Multilingual App Toolkit
I love this tool

If you are using Xamarin.Android and Xamarin.iOS to build your apps, you can use MAT to generate Android and iOS string resource files.

Besides translating the text that is compiled into the app, I spend time discussing how to manage language resources coming from web services.  Once again, the MAT is handy for managing language resources server side.

The sample app (Spanish version)

There is a chapter that builds a Xamarin.Forms app from scratch and then localizes it for the Chinese, Spanish, and German languages.  For the German translations, I was helped out by one of my co-workers, David Krings.  David is a native German speaker with technical writing experience.

When translating language resources, a fellow co-worker is one of the best resources.  Besides knowing the language, that person will know the domain of the app.  Context is very important with language translation.

Consider a string resource of “boot” in English and you need to translate it to the Polish language.  You need to know which meaning of “boot” to translate.  In US English, it means a type of footware and it could be translated as “kalosze”.  It could also mean to start up a process, which could translate to “rozruchu”.  In UK English, it’s the part of the car where you store things, that translates more or less as “bagażnika”.  Without the context, it’s easy to get the wrong translation.

For the Chinese and Spanish translations, I used a commercial translation company called SmartCAT.  Two of their translators, Max Diaz and Максим Морковкин, did the Spanish and Chinese translations.  I like how that SmartCAT works and recommend it to anyone that needs translation services performed.  SmartCAT’s Head of Community, Vova Zakharov, arranged for the translation services and at the last too.

When you have your app translated, you will need to have done by someone with the language expertise.  Machine translation has come a long way, but it’s better to do it right.

Besides language translation, the book covers how to deal with numbers, dates, currency, and other country/regional formatting.  The nice thing about using the .NET Framework is that most of the heavy lifting is done for you.  You don’t have to worry about how to handle it with one OS and then figure it out for another OS.

The source code for the book is up on Github.  You can access it from Apress/cross-platform-localization-xamarin.  You will want to download or clone the repo to follow along with the book.

And while I’m thanking people, I wanted to give a shout out to the technical reviewers.  Craig Dunn is part of the Xamarin team that is now part of the Microsoft team. He made this book much, much better.  Cameron Lerum created the Multilingual App Toolkit, which made most of this book possible.  Between the subject expertise and technical writing skills, I could not have had any better reviewers for this book.

Quick Powershell tip for avoiding file name collisions when you have images from multiple cameras

My wife and I both have Sony mirrorless cameras. She has an Alpha 5000 and I have a NEX-6. I back up all of our images to my home dev box, and then to a QNAP NAS server. The images are stored in folders based on year and month.

The date based filing makes it somewhat easy to find the images, but when you have images from multiple cameras, you run the slight (but real) risk of having both cameras save an image with the same file name. Both camera use the standard DSCXXXXX naming scheme, where XXXXX is number that is being incremented by the camera.  It’s also a little difficult to quickly separate her images from mine.

So what I do is to use a quick Powershell command to rename my wife’s images. I replace the “DSC” part of the file name with her initials. So lets say that you wanted to replace the “DSC” with “ABC”, a simple way would be with the following command:

Get-ChildItem -Filter “DSC*.*” | 
  Rename-Item -NewName {$ -replace ‘DSC’,’ABC’ }

That will get all of the files in the current folder that match dsc.* and pass that list to the rename-item command. The rename-item will iterate through that list and replace all of the occurrences of “DSC” with “ABC”. If you only shoot JPEG files, you can filter on “DSC*.JPG”. We shoot RAW plus JPEG, so we used the wild card extension to get all of the files.

Now can I store her images with mine and they all get backed up to multiple places.  I also back them up to multiple cloud providers, because sometimes it falls down.

If you want to explicitly do just a couple of file extensions, you can use the -Include option instead of -Filter. When you use the -Include option, you must also use the -Path option and set the path. One would be like this.

Get-ChildItem -Path .\\ -Include *.jpg, *.raw | 
  Rename-Item -NewName {$ -replace ‘DSC’,’ABC’ }

Modifying test data for privacy

Sometimes I get actual live data from a client to track down a bug that only happens with their data.  That data will contain student records and we don’t like to have live student laying around.   We can use TDE to encrypt the data at rest, but if I’m sharing that data with other developers, I want to scrub identifying details from data set.

For the most part, I just need to replace the first and last names from student table.  I could set both the first and last names to “Gank“, but if every record looks the same, it can be hard to see how the bug manifests itself.  I could set both attributes to the record id value for the record, but I find that hard to look at after a while.

What I end up is writing some sort of reubenizer code.  The reubenizer changes the first and last names to some variation of “Reuben”.

The Patron Saint of all that is Reuben.  Dave Madden as Reuben Kincaid

Let’s create a fake table to represent the student data to modify.

declare @Student TABLE
    RecordID varchar(4),
    FirstName varchar(80),
    LastName varchar(80),
    Gender char(1)

-- Push in some fake data
insert into @Student (RecordID, FirstName, LastName, Gender) values (1, 'Joe', 'Smith', 'M');
insert into @Student (RecordID, FirstName, LastName, Gender) values (2, 'Joel', 'Smith', 'M');
insert into @Student (RecordID, FirstName, LastName, Gender) values (3, 'Jane', 'Smith', 'F');
insert into @Student (RecordID, FirstName, LastName, Gender) values (4, 'Linda', 'Tokken', 'F');
insert into @Student (RecordID, FirstName, LastName, Gender) values (5, 'Samantha', 'Queen', 'F');
insert into @Student (RecordID, FirstName, LastName, Gender) values (6, 'Steve', 'Burton', 'M');
insert into @Student (RecordID, FirstName, LastName, Gender) values (7, 'Doug', 'Francis', 'M');
insert into @Student (RecordID, FirstName, LastName, Gender) values (8, 'Linda', 'McLinda', 'F');
insert into @Student (RecordID, FirstName, LastName, Gender) values (9, 'Paul', 'Davis', 'M');
insert into @Student (RecordID, FirstName, LastName, Gender) values (10, 'Ann', 'Davis', 'F');

Running those statements will generate a result set that looks like this

RecordID FirstName        LastName         Gender
-------- ---------------- ---------------- ------
1        Joe              Smith            M
2        Joel             Smith            M
3        Jane             Smith            F
4        Linda            Tokken           F
5        Samantha         Queen            F
6        Steve            Burton           M
7        Doug             Francis          M
8        Linda            McLinda          F
9        Paul             Davis            M
10       Ann              Davis            F

The first thing I do is create a table with a set of surname prefixes.  These prefixes will be used with the string “Reuben” to create the new last names

 -- Create a table with some surname prefixes.  
-- We'll pick the prefix from the last digit of the record id of the student.
-- We only do this so we don't have to look at the same name for every row

declare @Reuben TABlE
    RecordID varchar(4),
    LastName varchar(16)

-- Pick 10 different prefixes
insert into @Reuben (RecordID, LastName) values ('1', 'Mc');
insert into @Reuben (RecordID, LastName) values ('2', 'de ');
insert into @Reuben (RecordID, LastName) values ('3', 'Del');
insert into @Reuben (RecordID, LastName) values ('4', 'St ');
insert into @Reuben (RecordID, LastName) values ('5', 'Van ');
insert into @Reuben (RecordID, LastName) values ('6', 'Le ');
insert into @Reuben (RecordID, LastName) values ('7', 'La');
insert into @Reuben (RecordID, LastName) values ('8', 'Lo');
insert into @Reuben (RecordID, LastName) values ('9', 'O''');
insert into @Reuben (RecordID, LastName) values ('0', '');

Now it’s time to create the update statement to reubenize the names. To get the surname prefix, we’ll get the last digit of the record id. That will slice up the students into 10 different sets of last names. There are other ways of doing this, this one is quick and simple. You could do the same thing with the first name, but in this case, I’m just going to use “Reuben” for the boys and “Reubenette” for the girls, and tack on that last digit.

To make the code a little cleaner, I use a Common Table Expression (or CTE) to create a calculated field for the last digit of the record id. If you are not familair with CTE’s, they let you build temporary result sets that only exist within the context of the SQL expression that they are in.  I blogged about using a CTE here and there.

That update statement would look something like this

-- Using a CTE allows us to calculate the last digit just once
WITH cte (recordid, offset) 
     AS (SELECT recordid, 
                RIGHT(Cast(s.recordid AS VARCHAR), 1) AS OffSet 
         FROM   @student s) 
SET    s.lastname = Concat(r.lastname, 'Reuben'), 
       s.firstname = CASE s.gender 
                       WHEN 'M' THEN Concat('Reuben-', cte.recordid) 
                       ELSE Concat('Reubenette-', cte.recordid) 
FROM   cte 
       JOIN @student s 
         ON cte.recordid = s.recordid 
       JOIN @Reuben r 
         ON r.recordid = cte.offset; 

After running that update statement, the result set will look like this

RecordID FirstName        LastName         Gender
-------- ---------------- ---------------- ------
1        Reuben-1         McReuben         M
2        Reuben-2         de Reuben        M
3        Reubenette-3     DelReuben        F
4        Reubenette-4     St Reuben        F
5        Reubenette-5     Van Reuben       F
6        Reuben-6         Le Reuben        M
7        Reuben-7         LaReuben         M
8        Reubenette-8     LoReuben         F
9        Reuben-9         O'Reuben         M
10       Reubenette-10    Reuben           F

The records are no longer recognizable and are distinct enough to allow me to debug the problem. This doesn’t work for every kind of data element, but it allows me to work with and share live data with out displaying any personal identification.

Found the cause for ADB error message “Could not open interface: e00002c5”

Frustration with Computers
Frustration by Peter Alfred Hess

I spent way too much time tracking down a problem that prevented ADB on my Macbook from seeing my phone.  While at the Xamarin Evolve conference last week, I hooked up my Nexus 6P to my Macbook Pro to try some Xamarin.Forms code.  I connected the phone and checked the box on the phone that prompted to allow debugging.  The Xamarin Studio IDE did not see the phone.

So I opened up a terminal window and start issuing ADB commands.  If you don’t do Android development, ADB stands for Android Debug Bridge and it provides the communication channels that allow a development tool to talk to an Android device or emulator.

I ran the command “adb devices” and it came back with “no devices”.  To get more information, I ran the command to stop the ADB service and restart it:

adb kill-server ; adb devices

That generated the following output

List of devices attached
* daemon not running. starting it now on port 5037 *
adb I 2192 55546 usb_osx.cpp:259] Found vid=18d1 pid=4ee2 serial=8XV7NXXXXXXXXXXX
adb I 2192 55546 usb_osx.cpp:259]
adb E 2192 55546 usb_osx.cpp:331] Could not open interface: e00002c5
adb E 2192 55546 usb_osx.cpp:265] Could not find device interface
* daemon started successfully *

The 8XV7NXXXXXXXXXXX is an obfuscated version of my phone’s serial number.  So ADB could see that I had a device connected.  The Android File Transfer app could also see the phone, so the connection was there.  Just something was interfering with ADB.

Since I was at a Xamarin conference, I grabbed a Xamarin Android engineer and we started digging in.  First thing was to use his phone and cable, to rule out my phone and/or cable as being the problem.  We saw the same problem with his phone.   So we tried the obvious steps:

  • Rebooted the Macbook.  Nope
  • Used the other USB port. Nope
  • Downloaded a new copy of the Android SDK. Nope
  • Ran an Android emulator.  That worked, indicating the problem was between the USB port and ADB
  • Grabbed a more senior Android engineer who told us to go look at the ADB source code.

The engineers had classes to assist with so it was down to just me.  I went and looked at the source code to the usb_osx.cpp unit from ADB on Github.  The line numbers didn’t match up exactly, but the error was that it literally could not open the USB port.  That meant another process had it’s greedy little mitts on my USB port

I rebooted the Macbook in safe mode.  That would run OS X with out the 3rd party apps.  Sure enough, ADB was able to connect just fine.  That was the first clue, then some people in real life and on the Internets suggested that it may be a tethering app.

Apparently at some point last year, I installed EasyTether,  I don’t use it, but had neglected to uninstall it.  And it’s documented on the EasyTether FAQ page that it will break ADB’s connection with devices.  I pointed Finder to /System/Library/Extensions and sure enough, I had EasyTetherUSBEthernet.kext installed.  I could have used kextunload to just unload the EasyTether extension, but I decided to just yank it out.  I dragged it over to the Trashcan and rebooted.

I plugged my phone in and ADB saw it.

This made me happy

I could use my phone for debugging again.  I use Android emulators about half of the time, but when I want to see how the app behaves on actual device, there’s no beating the real thing.  Plus, debugging touch on a Mac just plain sucks.

I’ve been using Vysor to mirror the Android screen to the desktop and it works great.  I can use the actual device’s screen or control it from the Macbook.    If you are doing a presentation and want to show what is running on your Android device, get Vysor.  It’s a Chrome and and uses ADB, so it works on Mac, Windows, and probably Linux.

Adding Google Play Services to Visual Studio Android Emulator

Out of the box, the Visual Studio Android Emulator (and the Genymotion emulator, and the Xamarin Android Player) does not support Google Cloud Messaging (GCM) push notifications.  The reason for this is that GCM is part of the Google Play Services.  And the Google Play Services are not included in the virtual machine (VM) images that the Visual Studio Android Emulator uses.

The typical Android device starts with a base Android stack that comes from the Android Open Source Project (AOSP).  Device OEMS (Samsung, Huawei, LG, etc) then license the Google Play Services from Google.  On top of that, the OEMs add any customizations that they do to Android.

Google does not allow Microsoft/Genymotion/Xamarin to include the Google Play Services with their builds from the AOSP.  Enough developers have put together versions of the package so that it’s a fairly easy process to install. They are commonly packaged under the name “GApps”.

Run the Visual Studio Emulator for Android from the Start Menu.  If you run it from VS, you may not be able to install firmware packages.  Then create a new VM.  For this example, we’ll create an Android 5.1 VM.  I tried this with Android 6 and it did not work with the GApps packages that I was able to obtain.

Emulation Manager

If you are using an existing VM, you’ll need to know which CPU architecture or ABI that the VM is running under.  Thanks to a tip from the nutty people at Intel, you can execute an ADB command to see what is on board.

adb shell getprop ro.product.cpu.abilist

Also see the documentation for the Build class.

SInce we created the VM, we know it’s Android 5.1.  If you were working with VM and were not sure of the version, you can check via the Android Settings app or from the command line with the adb command.

adb shell getprop

While we are checking stuff with ADB, the following command will return the SDK version

adb shell getprop
Results from the ADB commands
Results from the ADB commands

First up is the installation of an ARM translator. The VS Android Emulator gets its speed by running an x86 version of Android. The Google Play Services are usually packed up already compiled for ARM. The ARM translator lets ARM code run on an x86 image. This is usually packaged up in a .zip named ARM Translation v1.1.

Installing is easy, drag the .zip on to a running Android VM and follow the prompts.

The dialog that appears after drag and drop the ARM Translator package onto the Android VM

If it didn’t reboot the VM, reboot it to be safe.  Multiple web sites have a copy of this file.  I downloaded one from the Tech Bae blog.

ARM Translator installed

Since we have Android 5.1, we need a GApps package for Android 5.1.  There are a few places where you can download a package from, but not all of them may work.  I was hoping to to use the packages on the Open Gapps project.  None of their packages would install into my VMs.  They all reported an invalid folder error message.

The file sets available from TeamAndroid should install without any problems.  I downloaded one named  The “lp” in the file name stands for Lollypop, the code name for Android 5.

Drag the gapps package and drop it on your Android VM.  You should get a dialog like this:

Click the install button and let it do it’s thing.  After it completes, it should shutdown.  Restart it from the Emulator Manager.  After Android starts up, you may see a “Optimizing app X of Y” dialog.  When Android versions upgrade, the apps all need to tuned for the new version.  This is normal.

When that is all done, you should see  the Google Play icon in the app drawer.  Launch the app and provide your Google account information.  If you see an endless busy indicator, let it go for a minute, then close and restart Google Play.

You may see an error message about Google Play services having stopped.  That is normal and should go away once the Google Play services have been updated.

After installing GApps, some (many) Google apps and services will probably crash. Do not be alarmed, that is perfectly normal. Most of the files are out of date.

Get the Google Play app to run long enough for you to login and it will start updating.  To force Google Play to update itself, do the following (from Android Central):

  1. Launch Google Play
  2. Slide out the menu
  3. Tap on Settings
  4. Scroll to the bottom and tap Build version

If a newer version is a available, you’ll see a dialog with that information.

At that point, your Android VM will support push notifications.  You can install of Google Play apps like the Maps application.  These steps were tested with Visual Studio Android Emulator but they should work more or less in the same way with the Genymotion and Xamarin emulators.

The Open GApps page looks like it is a promising location to get GApp packages, they have a list of variants.  Basically each variant has more or less of the Google Apps.  To keep things simple, I wanted to use the stock version.  There is a naming scheme for gapps distributions.  It follows the pattern or a subset of that pattern

For this example, I had downloaded the full version of the x86 Android 6.0 GApps.  that came down with the following command line.

It wouldn’t install, but that is the accepted pattern for naming GApp packages.  It (and the ARM version) error-ed out with an invalid directory message.  Hopefully this will be addressed in an update to the Visual Studio Android Emulator.

This article’s banner image comes from Arena4G.

Thoughts on One Trick Ponies

When your company is a one trick pony (Apple), that pony (the iPhone) better be a compelling trick.  ZDNet’s Ed Bott just wrote an article that shows where Apple, Google, and Microsoft gain their income from.  While Google predictably gets most of the money from advertising and Microsoft has pivoted from the Windows company to the Services company, Apple remains as the company that sells the iPhone with other things.

With 68% of Apple’s income coming from the iPhone, protecting that product line is a big deal for Apple.  With the relative maturity of the Android platform, it’s incumbent on Apple to keep the iPhone as the premium device.  It has to be different and it has to be better in order for Apple to keep that revenue stream high.

There’s an argument that I have had with a few other developers about mobile development tools that let you write once, deploy everywhere.  Tools like Cordova, where the screens are defined in HTML and the code in JavaScript.  I’ve been told that I should be using that instead of tools that let me write to the native platform.  Xamarin tools in my case.  The Cordova (and other web like development tools) are a threat to Apple.  If you can write an app with Cordova and it runs the same way on Android as it does on the iPhone, what is the point in paying a premium for Apple hardware?

That’s why Apple continues to add functionality and services that doesn’t exist on other platforms.  Features like 3D Touch or FaceTime.  Remember when FaceTime was announced?  Steven Jobs said it would be an open industry standard.  That was in 2010.  Six years later Facetime is still a closed platform.  If FaceTime ran on Android, customized Androids would be the rage at schools, not the iPhone.  Apple is never going to open up FaceTime.  But they will continue on enhancing that one trick.

Get AMPed for faster page loading on mobile

I just updated this blog to use the Accelerated Mobile Pages (AMP) plugin for WordPress.  AMP is an open source project designed to optimize page content for mobile pages.  It uses a subset of HTML and some JavaScript to trim down the content of a web page.  Early testing has shown that AMP pages load four times faster and use eight times less data than a traditional web page.

AMP filters out 3rd party JavaScript code (aside from the AMP code), which basically takes out every analytics package.  Iframe and object tags are stripped out.  The HTML5 multimedia tags (img, video, audio) are replaced with custom elements (amp-img, amp-video, amp-audio).  On a page that uses the AMP runtime, a hidden link is added that provides the URL to AMP optimized version of the web page.  With the WordPress plugin, the AMP page is the original URL with “/amp” appended to it.  This will work on any hosted WordPress site.  Self hosted sites would need to add the plugin.

For this blog post, if you want to see the AMP version, click here.  It will load much faster, but without any special styling.  The banner image will be gone.  I like to use images from the Getty collection and as part of the terms of use, you have to embed the image with their custom template and that template puts the image in an iframe.  And since iframes are forbidden in AMP HTML, good bye image.

The AMP runtime manages the amp-* HTML tags and can manage the loading and unloading of the resources. CSS is also limited for performance reasons.  That trade off gives you the performance boost on mobile.  AMP is already being used by sites like Pinterest, Twitter, New York Times, The Verge.

AMP is not for everyone.  It is basically serving up a subset of your experience and you will lose custom animations, analytics, etc.  So where is it useful?  When you want a page to load as fast as it can.  Like a status page for service.  Google is already indexing AMP pages.  While it’s not currently giving preference to AMP pages, it does rank pages by page load.  AMP is open source and you can view the code on GitHub in the ampproject repository.

Generating new row numbers in SQL based on a starting value

One of our support people came to me with an interesting task.  He needed to add rows from a SQL Server table to another table and populate a row number type of field.  That field needed to be generated by his SQL statements and start with the next number after the highest value already in the table.  And he couldn’t use an autoincrement field.

My first thought was to read the new values into a table variable with an column for that row number field.  Then iterate over that table with a cursor and populate that column.  That was a very bad thing and was quickly banished.  So I thought about it a bit and sent him the following example.

-- The following temporary tables represent the 
-- the data to be worked with.  They are temp
-- tables only to illustrate the code
create table #BaseTable
    ID int,
    Name varchar(64)

create table #NewData
    Name varchar(64)

-- Lets throw some sample data into the base
-- table.
insert #BaseTable(ID, Name) values (1, 'Joe')
insert #BaseTable(ID, Name) values (2, 'Jim')
insert #BaseTable(ID, Name) values (3, 'Jay')
insert #BaseTable(ID, Name) values (4, 'John')

-- Lets populate the data table that we want
-- to get the data from
insert #NewData(Name) values ('Ken')
insert #NewData(Name) values ('Keith')
insert #NewData(Name) values ('Kevin')

-- Declare a variable to hold the maximum
-- value of our row number field
declare @LastRow int

-- Assign the max value to our variable
set @LastRow = (select MAX(ID) from #BaseTable)

-- Add the rows into the destination tabkle
insert into #BaseTable(id, name)
-- Use the rank() function to generate row
-- numbers for the rows that we are adding
-- and add the @LastRow to get the next highest
-- values
select rank() over (order by n.Name) + @LastRow as rank, n.Name
from #NewData n
order by rank

-- Show our merged results
select * from #BaseTable

-- Example over, cleanup
drop table #NewData
drop table #BaseTable

To generate the row numbers from the merge table, we use the rank() function.  Rank() returns the rank of each row within the partition of the result set.  We specify the partition with the “over (order by” clause.  If you have multiple fields to make a row unique, you would need to specify each field in the over clause.

FBI vs Apple vs the Consequences

iPhone 5C, FBI Edition

There has been a lot of conversations about the court order that the FBI has filed on Apple.  If you are following this story and have not yet read the court order, you should read it now.

The FBI is asking for Apple to disable the code that slows down and locks access to the device when too many incorrect pass codes are entered. They are not asking for Apple to decrypt anything and have stated that Apple could keep possession of the device.  The Trail of Bits blog has a very write up of how Apple could comply with this request.

My initial thought was that this was a reasonable request.   What tipped me over to Apple’s view was what will happen afterwards.  Once this becomes a precedent, other countries could make the same request of Apple.  Countries with a less than stellar record of civil rights. The 800 lb gorilla in that room would be China. The same people who brought us the Golden Shield Project (aka the Great Firewall of China) would now have the opportunity to add new laws requiring backdoors into all mobile devices sold in their country. You may have seen the tweet from Snowden on this subject:

There are other ways that the FBI can get some of information that would be on the phone.  I would assume that they have already subpoenaed the mobile carrier to get the phone records.  That would tell the FBI who was talking to Farook and Malik.  According to the court order, the phone was on the Verizon network.  If Farook and/or Malik were sending SMS text messages, then Verizon has those messages on file and can supply them in response to a court order.  If they were using iMessage and were backing the phone up to iCloud, Apple could hand over that information via court order.  There’s a good article on that goes over the multiple ways information can be accessed from an iPhone.

Hard cases make bad law.  While I sympathize with the reasons that the FBI is making this request, the long term consequences would be worse.  We  face the real risk of having weaker security in mobile devices.  At the end of the day, Apple is making the right choice in opposing the court order.  It sets a bad legal precedence and they should fight it all the way to the Supreme Court.

Use Powershell to batch rename files from digital cameras

If your household is like my household, then you’ll have more than one digital camera laying around.  My wife and I have similar Sony cameras (NEX-6 and an a5000).  For easy of management and the sake of my own sanity, I back up the images from both cameras together.

I store my images in a chronological order.  A folder for each year, then sub folders for each month.  It’s simple to manage and it’s easy to find pictures.  When daughter(0) asks where the pictures are from last year’s lake vacation, I can tell her it’s in “2015\7”.   There are times where I create folders for special purposes, but by and large the collection is in chronological order.

Since Sony prefixes every image with the letters DSC, I wanted a way differentiate between my images and my wife’s images.  I also wanted to eliminate any chance of two images having the same name.  There many ways of doing batch file renaming.  I wanted something simple.

That brings us to Powershell.  There’s very little that you can’t do with Powershell and a lot resources for it.  In this case, a batch rename is trivial:

# Pipe the contents of the DIR command
# to the Rename-Item command and do text
# replacement
Dir *.jpg, *.arw | 
  Rename-Item –NewName { $ –replace “DSC“,”ABM” }

That command will take all of the files in the current folder with the .JPG and .ARW (Sony’s RAW file) extension and rename any of them that have “DSC” in the name to use “ABM”. This will replace the “DSC” in any part of the filename. If you want to just match files that start with “DSC”, then you could RegEx pattern matching. Since I didn’t need that level of pattern matching, I chose not to dive down that rabbit hole.

A work in progress

%d bloggers like this: