# Japanese Romanization Standards for Language Learning Templates
**Document Version:** 1.0
**Date:** October 6, 2025
**Author:** Alan William Preston
**Purpose:** Standardized utilities for handling Japanese text in interactive web-based language learning templates
---
## 1. Overview
This document provides standardized JavaScript functions for handling Japanese romanization (Hepburn system) in interactive language learning templates. These utilities address the common challenge of accepting user input that may not include proper macrons or diacritical marks.
---
## 2. Romanization System
**Standard:** Modified Hepburn Romanization System
### 2.1 Long Vowel Representations
Users may input long vowels in multiple ways. All variations should be accepted:
| Standard (Macron) | Alternative 1 | Alternative 2 | Hiragana | Example Word |
|-------------------|---------------|---------------|----------|--------------|
| ō | ou | o | おう/おお | Tokyo (Tōkyō) |
| ū | uu | u | うう | tsūkin (通勤) |
| ā | aa | a | ああ | okāsan (お母さん) |
| ē | ee / ei | e | ええ/えい | sensei (先生) |
| ī | ii | i | いい | oishii (おいしい) |
### 2.2 Special Characters
| Character | Variants to Accept | Notes |
|-----------|-------------------|-------|
| n' | n | Apostrophe distinguishes syllabic n from na/ni/nu/ne/no |
| ' (apostrophe) | (none) | Generally removed in normalization |
---
## 3. Core Utility Functions
### 3.1 Basic Normalization Function
```javascript
/**
* Normalizes romanized Japanese text to handle various input formats
* Removes macrons, converts long vowel alternatives, removes spaces
* @param {string} text - The romanized Japanese text to normalize
* @returns {string} - Normalized text in lowercase without spaces
*/
function normalizeRomaji(text) {
return text.toLowerCase()
.replace(/ō/g, 'o').replace(/ou/g, 'o')
.replace(/ū/g, 'u').replace(/uu/g, 'u')
.replace(/ā/g, 'a').replace(/aa/g, 'a')
.replace(/ē/g, 'e').replace(/ee/g, 'e').replace(/ei/g, 'e')
.replace(/ī/g, 'i').replace(/ii/g, 'i')
.replace(/n'/g, 'n').replace(/'/g, '')
.replace(/\s+/g, '').trim();
}
```
### 3.2 HTML/Ruby Tag Stripping Function
```javascript
/**
* Strips HTML tags (especially ruby/rt tags) from Japanese text
* Useful for comparing user input against kanji+furigana content
* @param {string} html - HTML string containing ruby tags
* @returns {string} - Plain text content without HTML
*/
function stripHTML(html) {
const temp = document.createElement('div');
temp.innerHTML = html;
return temp.textContent || temp.innerText || '';
}
```
### 3.3 Romaji Comparison Function
```javascript
/**
* Compares two romanized strings with normalization
* @param {string} input - User input string
* @param {string} correct - Correct answer string
* @returns {boolean} - True if normalized versions match
*/
function compareRomaji(input, correct) {
const normalizedInput = normalizeRomaji(input);
const normalizedCorrect = normalizeRomaji(correct);
return normalizedInput === normalizedCorrect;
}
```
### 3.4 Japanese Script Comparison Function
```javascript
/**
* Compares Japanese script input (hiragana/katakana/kanji)
* Strips spaces and HTML for accurate comparison
* @param {string} input - User input in Japanese script
* @param {string} correctHTML - Correct answer (may contain ruby tags)
* @returns {boolean} - True if content matches
*/
function compareJapanese(input, correctHTML) {
const cleanCorrect = stripHTML(correctHTML).replace(/\s+/g, '');
const cleanInput = input.replace(/\s+/g, '');
return cleanInput === cleanCorrect;
}
```
### 3.5 Universal Answer Checking Function
```javascript
/**
* Checks user answer against both romaji and Japanese script
* Accepts either format as correct
* @param {string} userInput - What the user typed
* @param {string} correctRomaji - Correct romaji answer
* @param {string} correctJapanese - Correct Japanese script (may have HTML)
* @returns {boolean} - True if answer is correct in either format
*/
function checkJapaneseAnswer(userInput, correctRomaji, correctJapanese) {
return compareRomaji(userInput, correctRomaji) ||
compareJapanese(userInput, correctJapanese);
}
```
---
## 4. Complete Utility Object (Recommended Implementation)
For cleaner code organization, use this object-oriented approach:
```javascript
/**
* JapaneseUtils - Comprehensive utilities for Japanese language templates
* Usage: JapaneseUtils.checkAnswer(userInput, romaji, japanese)
*/
const JapaneseUtils = {
/**
* Normalizes romanized Japanese text
*/
normalizeRomaji: function(text) {
return text.toLowerCase()
.replace(/ō/g, 'o').replace(/ou/g, 'o')
.replace(/ū/g, 'u').replace(/uu/g, 'u')
.replace(/ā/g, 'a').replace(/aa/g, 'a')
.replace(/ē/g, 'e').replace(/ee/g, 'e').replace(/ei/g, 'e')
.replace(/ī/g, 'i').replace(/ii/g, 'i')
.replace(/n'/g, 'n').replace(/'/g, '')
.replace(/\s+/g, '').trim();
},
/**
* Strips HTML tags from text
*/
stripHTML: function(html) {
const temp = document.createElement('div');
temp.innerHTML = html;
return temp.textContent || temp.innerText || '';
},
/**
* Compares romanized strings with normalization
*/
compareRomaji: function(input, correct) {
const normalizedInput = this.normalizeRomaji(input);
const normalizedCorrect = this.normalizeRomaji(correct);
return normalizedInput === normalizedCorrect;
},
/**
* Compares Japanese script strings
*/
compareJapanese: function(input, correctHTML) {
const cleanCorrect = this.stripHTML(correctHTML).replace(/\s+/g, '');
const cleanInput = input.replace(/\s+/g, '');
return cleanInput === cleanCorrect;
},
/**
* Universal answer checker - accepts both romaji and Japanese
*/
checkAnswer: function(userInput, correctRomaji, correctJapanese) {
return this.compareRomaji(userInput, correctRomaji) ||
this.compareJapanese(userInput, correctJapanese);
},
/**
* Validates if input contains Japanese characters
*/
containsJapanese: function(text) {
return /[\u3040-\u309F\u30A0-\u30FF\u4E00-\u9FAF]/.test(text);
},
/**
* Determines if input is romaji or Japanese script
*/
isRomaji: function(text) {
return !this.containsJapanese(text);
}
};
```
---
## 5. Implementation Examples
### 5.1 Basic Quiz Answer Checking
```javascript
// In your quiz checking function:
function checkQuizAnswer() {
const userInput = document.getElementById('quiz-input').value.trim();
const correctRomaji = currentItem.romaji; // e.g., "ohayō"
const correctJapanese = currentItem.ja; // e.g., "<ruby>お早う<rt>おはよう</rt></ruby>"
if (JapaneseUtils.checkAnswer(userInput, correctRomaji, correctJapanese)) {
// Correct answer!
showSuccessFeedback();
} else {
// Incorrect
showErrorFeedback();
}
}
```
### 5.2 Alternative Implementation (Individual Functions)
```javascript
// Without using the JapaneseUtils object:
function checkQuizAnswer() {
const answer = document.getElementById('quiz-input').value.trim();
const correctRomaji = normalizeRomaji(currentItem.romaji);
const correctJa = stripHTML(currentItem.ja).replace(/\s+/g, '');
const userAnswer = answer.replace(/\s+/g, '');
const userRomaji = normalizeRomaji(answer);
if (userRomaji === correctRomaji || userAnswer === correctJa) {
// Correct!
} else {
// Incorrect
}
}
```
### 5.3 Handling Apostrophes in Data
**Important:** When defining Japanese vocabulary data with apostrophes, use double quotes:
```javascript
// INCORRECT - Will cause syntax error:
{ romaji: 'shin'ya', ja: '深夜' }
// CORRECT - Use double quotes for strings containing apostrophes:
{ romaji: "shin'ya", ja: '深夜' }
// ALSO CORRECT - Escape the apostrophe:
{ romaji: 'shin\'ya', ja: '深夜' }
```
---
## 6. Ruby Tag Standards
### 6.1 Recommended Ruby Tag Format
For displaying kanji with furigana (reading aids):
```html
<ruby>真夜中<rt>まよなか</rt></ruby>
```
### 6.2 CSS for Ruby Tags
Include this CSS for proper ruby tag display:
```css
ruby {
font-size: 1em;
}
rt {
font-size: 0.6em;
color: #666;
}
```
### 6.3 Font Stack for Japanese
Recommended font-family for Japanese text:
```css
font-family: "Hiragino Sans", "Yu Gothic", "Meiryo", sans-serif;
```
---
## 7. Testing Matrix
### 7.1 Test Cases for normalizeRomaji()
| Input | Expected Output | Notes |
|-------|----------------|-------|
| Tōkyō | tokyo | Macrons removed |
| Tokyo | tokyo | Already normalized |
| Toukyou | tokyo | Double vowels converted |
| Ohayō gozaimasu | ohayogozaimasu | Macrons + spaces removed |
| shin'ya | shinya | Apostrophe removed |
| Kyōto | kyoto | Single macron |
| sensei | sense | ei → e conversion |
### 7.2 Test Cases for Japanese Script Matching
| Input (User) | Correct Answer | Should Match |
|--------------|----------------|--------------|
| まよなか | `<ruby>真夜中<rt>まよなか</rt></ruby>` | ✓ Yes |
| 真夜中 | `<ruby>真夜中<rt>まよなか</rt></ruby>` | ✓ Yes |
| mayonaka | まよなか | ✗ No (different functions) |
---
## 8. Common Edge Cases
### 8.1 Particle は (wa vs ha)
The particle は is pronounced "wa" but written "ha" in romaji:
- Accept both: "konnichiwa" and "konnichiha"
- Standard: "konnichiwa"
**Implementation:**
```javascript
.replace(/\bha\b/g, 'wa') // Convert standalone ha to wa
```
### 8.2 を (wo vs o)
The particle を is pronounced "o" but sometimes written "wo":
- Accept both: "arigatō" and "arigatou"
- Standard in Hepburn: "o"
### 8.3 Long vowels in different contexts
| Context | Standard | Alternative |
|---------|----------|-------------|
| おう | ō | ou |
| おお | ō | oo |
| えい | ē | ei |
| えぇ | ē | ee |
---
## 9. Quick Reference: Copy-Paste Code Block
```javascript
/* ============================================
JAPANESE ROMANIZATION UTILITIES
Version 1.0 - Copy this into your templates
============================================ */
const JapaneseUtils = {
normalizeRomaji: function(text) {
return text.toLowerCase()
.replace(/ō/g, 'o').replace(/ou/g, 'o')
.replace(/ū/g, 'u').replace(/uu/g, 'u')
.replace(/ā/g, 'a').replace(/aa/g, 'a')
.replace(/ē/g, 'e').replace(/ee/g, 'e').replace(/ei/g, 'e')
.replace(/ī/g, 'i').replace(/ii/g, 'i')
.replace(/n'/g, 'n').replace(/'/g, '')
.replace(/\s+/g, '').trim();
},
stripHTML: function(html) {
const temp = document.createElement('div');
temp.innerHTML = html;
return temp.textContent || temp.innerText || '';
},
checkAnswer: function(userInput, correctRomaji, correctJapanese) {
const normalizedInput = this.normalizeRomaji(userInput);
const normalizedCorrect = this.normalizeRomaji(correctRomaji);
const cleanJapanese = this.stripHTML(correctJapanese).replace(/\s+/g, '');
const cleanInput = userInput.replace(/\s+/g, '');
return normalizedInput === normalizedCorrect || cleanInput === cleanJapanese;
}
};
/* ============================================
END JAPANESE UTILITIES
============================================ */
```
---
## 10. Version History
| Version | Date | Changes |
|---------|------|---------|
| 1.0 | 2025-10-06 | Initial document creation |
---
## 11. Related Resources
- **Hepburn Romanization:** [Wikipedia](https://en.wikipedia.org/wiki/Hepburn_romanization)
- **Ruby Tag Specification:** [MDN Web Docs](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/ruby)
- **Japanese Character Unicode Ranges:**
- Hiragana: U+3040 to U+309F
- Katakana: U+30A0 to U+30FF
- Kanji: U+4E00 to U+9FAF
---
## 12. Notes for Template Development
1. **Always test with multiple input formats** (macrons, double vowels, no macrons)
2. **Use double quotes** when defining strings with apostrophes in JavaScript
3. **Include ruby tag CSS** for proper furigana display
4. **Consider mobile input** - Japanese keyboards may not have easy macron access
5. **Whitespace handling** - Remove spaces in both user input and correct answers
6. **Case sensitivity** - Always normalize to lowercase for comparisons
7. **Font selection** - Use Japanese-capable fonts in your CSS
---
**End of Document**